package com.thinkit.cms.service.admin;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.thinkit.cms.api.admin.*;
import com.thinkit.cms.api.site.SiteService;
import com.thinkit.cms.dto.admin.OrgDto;
import com.thinkit.cms.dto.admin.UserDto;
import com.thinkit.cms.dto.admin.UserRoleDto;
import com.thinkit.cms.entity.admin.UserEntity;
import com.thinkit.cms.mapper.admin.UserMapper;
import com.thinkit.core.base.BaseServiceImpl;
import com.thinkit.core.constant.Constants;
import com.thinkit.core.constant.SecurityConstants;
import com.thinkit.core.handler.CustomException;
import com.thinkit.nosql.annotation.CacheClear;
import com.thinkit.nosql.base.BaseRedisService;
import com.thinkit.utils.enums.UserFrom;
import com.thinkit.utils.model.ApiResult;
import com.thinkit.utils.model.PageDto;
import com.thinkit.utils.utils.Checker;
import com.thinkit.utils.utils.Md5;
import com.thinkit.utils.utils.PasswordGenerator;
import com.thinkit.utils.utils.ValidateCodeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class UserServiceImpl extends BaseServiceImpl<UserDto, UserEntity, UserMapper> implements UserService {

    @Autowired
    UserRoleService userRoleService;

    @Autowired
    MenuService menuService;

    @Autowired
    RoleMenuService roleMenuService;

    @Autowired
    OrgService orgService;

    @Autowired
    BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    BaseRedisService baseRedisService;

    @Autowired
    SiteService siteService;



    @Override
    public PageDto<UserDto> listPage(PageDto<UserDto> pageDto){
        IPage<UserDto> pages = new Page<>(pageDto.getPageNo(), pageDto.getPageSize());
        IPage<UserDto> result = baseMapper.listPage(pages, pageDto.getDto(),getUserId());
        PageDto<UserDto> resultSearch = new PageDto(result.getTotal(), result.getPages(), result.getCurrent(), Checker.BeNotEmpty(result.getRecords()) ? result.getRecords() : Lists.newArrayList());
        return resultSearch;
    }


    @Override
    public UserDto findUserByUsername(String username) {
        QueryWrapper<UserEntity> queryWrapper =new QueryWrapper<>();
        queryWrapper.eq("username", username);
        UserEntity userEntity=getOne(queryWrapper);
        return Checker.BeNotNull(userEntity)? T2D(userEntity):null ;// 查询用户账户信息
    }

    @Override
    @Transactional
    public boolean save(UserDto userDto) {
        handUser(userDto);
        String passWord = bCryptPasswordEncoder.encode(Md5.md5(userDto.getPassword()));
        userDto.setPassword(passWord).setId(id());
        if (super.insert(userDto)) {
            insertUserRole(userDto.getRoleIds(), userDto.getId());
        }
        return true;
    }

    private void handUser(UserDto userDto){
        if (Checker.BeNotBlank(userDto.getOrgId())){
            OrgDto orgDto=orgService.getByPk(userDto.getOrgId());
            if(Checker.BeNotNull(orgDto)){
                userDto.setOrgCode(orgDto.getOrgCode()).setOrgName(orgDto.getOrgName());
            }
        }
        checkUserIsExist(userDto);
    }

    private void checkUserIsExist(UserDto userDto){
        if(checkExist(userDto.getId(),"email",userDto.getEmail())){
            throw new CustomException(ApiResult.result(5006));
        }
        if(checkExist(userDto.getId(),"mobile",userDto.getMobile())){
            throw new CustomException(ApiResult.result(5008));
        }
        if(checkExist(userDto.getId(),"user_account",userDto.getUserAccount())){
            throw new CustomException(ApiResult.result(5007));
        }
    }

    private boolean checkExist(String id,String tableField,String val){
        if(Checker.BeBlank(tableField)||Checker.BeBlank(val)){
            return true;
        }
        UserDto user=baseMapper.checkUserIsExist(id,tableField,val);
        return Checker.BeNotNull(user);
    }

    @CacheEvict(value = Constants.cacheName, key = "#root.targetClass+'.getById.'+#p0")
    @Override
    @Transactional
    public boolean deleteByUserId(String userId) {
        if (userId.equals(getUserId())|| "1".equals(userId)) {
            throw new CustomException(ApiResult.result(5000));
        }
        userRoleService.deleteByUserId(userId);
        return deleteByPk(userId);
    }


    @CacheClear(clas = {MenuServiceImpl.class},method = "selectPermsByUid",
    keys = {"#clas.targetClass+'.'+#clas.method+'.'+#p0.id+'.*'",
    "#clas.targetClass+'.'+loadMenu+'.'+#p0.id",
    })
    @CacheEvict(value = Constants.cacheName, key = "#root.targetClass+'.getById.'+#p0.id")
    @Override
    public boolean update(UserDto userDto) {
        handUser(userDto);
        List<UserRoleDto> list = new ArrayList<UserRoleDto>();
        if (super.updateByPk(userDto)) {
            //根据用户id先删除所有的角色然后再分配
            Map<String, Object> param = new HashMap<>();
            param.put("user_id", userDto.getId());
            userRoleService.removeByMap(param);
            insertUserRole(userDto.getRoleIds(), userDto.getId());
            String key = SecurityConstants.PERMISSION_ALL+userDto.getId()+Constants.delRedisKey;
            baseRedisService.removeBlear(key);
        }
        return true;
    }

    private boolean insertUserRole(String[] roleIds, String userId) {
        List<UserRoleDto> list = new ArrayList<UserRoleDto>();
        for (String roleId : roleIds) {
            UserRoleDto userRole = new UserRoleDto();
            userRole.setRoleId(roleId).setUserId(userId).setId(id());
            list.add(userRole);
        }
        boolean res = userRoleService.insertUserRoleBatch(list);
        return res;
    }

    @Transactional
    @Override
    public boolean deleteByIds(List<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return false;
        }
        if(ids.contains("1") || ids.contains(getUserId())){
            throw new CustomException(ApiResult.result(5000));
        }
        for (String userId : ids) {
            userRoleService.deleteByUserId(userId);
        }
        return super.deleteByPks(ids);
    }

    @Override
    public boolean lockUsers(boolean justLock) {
        String pass= PasswordGenerator.genPass(12);
        pass=bCryptPasswordEncoder.encode(PasswordGenerator.genPass(12));
        return baseMapper.lockUsers(pass,justLock);
    }

    private boolean updateByIds(List<String> ids, Integer type) {
        if(type==0 || type==1){
            if (ids == null || ids.isEmpty()) {
                return true;
            }
            List<UserEntity> users = (List) listByIds(ids);
            if (Checker.BeNotEmpty(users)) {
                users.forEach(userEntity -> {
                    userEntity.setStatus(type);
                    if(type==1){
                        //baseRedisService.set(SecurityConstants.LOCK_ACCOUNT+ Constants.DOT+userEntity.getId(),1);
                    }else {
                       // baseRedisService.remove(SecurityConstants.LOCK_ACCOUNT+ Constants.DOT+userEntity.getId());
                    }
                });
                return updateBatchById(users);
            }
        }
        return true;
    }

    // @SentinelResource(value = "info")
    @Override
    public  Map<String,Object> info() {
        //获取用户信息
        UserDto userDto = baseMapper.getUserInfo(getUserId());
        Set<String> perms = menuService.selectPermsByUid(getUserId(),null);
        Map<String,Object> userMap = new HashMap<>(16);
        userMap.put("id",userDto.getId());
        userMap.put("name",userDto.getName());
        userMap.put("account",userDto.getUserAccount());
        userMap.put("orgName",userDto.getOrgName());
        userMap.put("email",userDto.getEmail());
        userMap.put("mobile",userDto.getMobile());
        userMap.put("sex",userDto.getSex());
        userMap.put("birth",userDto.getBirth());
        userMap.put("permissions",perms);
        String siteDef = siteService.getDefault();
        if(Checker.BeNotBlank(siteDef)){
            userMap.put("Default-Site",siteDef);
        }
        return userMap;
    }


    @Cacheable(value= Constants.cacheName, key="#root.targetClass+'.'+#root.methodName+'.'+#root.args[0]",unless="#result == null")
    @Override
    public UserDto getById(String id) {
        UserDto userDto = getByPk(id);
        List<String> list = userRoleService.selectRoleIdByUId(userDto.getId());
        if (Checker.BeNotEmpty(list))
            userDto.setRoleIds(list.toArray(new String[list.size()]));
        return userDto;
    }

    @Override
    public boolean batch(Integer type, List<String> ids) {
        if(Arrays.asList(0,1,-1).contains(type)){
            if (type == -1) {
                return deleteByIds(ids);
            } else {
                return updateByIds(ids, type);
            }
        }
        return false;
    }

    @Override
    public boolean updateUserInfo(UserDto userDto) {
        userDto.setId(getUserId());
        return super.updateByPk(userDto);
    }

    @Override
    public List<UserDto> getUserByOrgId(String orgId) {
        UserDto userDto = new UserDto();
        userDto.setOrgId(orgId);
        List<UserDto> userDtos = listDto(userDto);
        if (Checker.BeEmpty(userDtos)) {
            return Lists.newArrayList();
        }
        return userDtos;
    }

    @Override
    public void modifyPass(UserDto user) {
        UserDto userDto=getByPk(getUserId());
        if(!bCryptPasswordEncoder.matches(Md5.md5(user.getInitPass()),userDto.getPassword())){
            throw new CustomException(ApiResult.result(5005));
        }
        String passWord = bCryptPasswordEncoder.encode(Md5.md5(user.getNewPass()));
        userDto.setPassword(passWord);
        updateByPk(userDto);
    }

    @Override
    public ApiResult resetPass(String id) {
        UserDto userDto=getByPk(id);
        if(Checker.BeNull(userDto)||"1".equals(userDto.getId())){
            throw new CustomException(ApiResult.result(7002));
        }
        String pass= PasswordGenerator.genPass(8);
        String passWord = bCryptPasswordEncoder.encode(Md5.md5(pass));
        userDto.setPassword(passWord);
        updateByPk(userDto);
        String key = SecurityConstants.ERROR_INPUT_PASS+ UserFrom.PLAT_USER.getClientId()+Constants.DOT +userDto.getUserAccount();
        baseRedisService.remove(key);
        return ApiResult.result(pass);
    }

    @Override
    public Set<String> selectRoleSignByUserId(String uid) {
        Set<String> signs=baseMapper.selectRoleSignByUserId(uid);
        return Checker.BeNotEmpty(signs)?signs:new HashSet<>(16);
    }

    @Override
    public List<String> getUserIdsByOrgId(String orgId) {
        List<String> userIds = baseMapper.getUserIdsByOrgId(orgId);
        return Checker.BeNotEmpty(userIds)?userIds:Lists.newArrayList();
    }

    @Override
    public UserDto loadUserByUsername(String userAccount) {
        return baseMapper.loadPlatUserByUsername(userAccount);
    }

    @Override
    public ApiResult verifyCode() {
         Map<String,String> params = new HashMap<>();
         String uuid = UUID.randomUUID().toString();
         ValidateCodeUtil.Validate validate = ValidateCodeUtil.getRandomCode();
         baseRedisService.set(Constants.VerifyCode+uuid,validate.getValue(),300L);
         params.put("uid",uuid);
         params.put("code",validate.getBase64Str());
         return ApiResult.result(params);
    }

}
